/* 
 * Author: Vassilis
 */

#include "LevelMap.h"
#include "Rect.h"
#include "gui/Graphics.h"
#include "sdl/Color.h"
#include "sdl/Application.h"
#include "Grid.h"
#include "sdl/GLColor.h"
#include <iostream>
#ifdef DSDL_GFX_ENABLED
#include <SDL/SDL_gfxPrimitives.h>
#endif

LevelMap::LevelMap()
{
	m_grid = NULL;
	m_background = NULL;
	m_width = m_height = 0;
	m_tileWidth = m_tileHeight = 0;
	SetAlwaysVisible( true );
	m_scale.Set( 1.0, 1.0 );
}

LevelMap::~LevelMap()
{
	delete m_grid;
	delete m_background;
}

void LevelMap::SetSize(unsigned sWidth, unsigned sHeight)
{
	if( m_grid != NULL )
	{
		delete m_grid;
	}

	m_width = sWidth;
	m_height = sHeight;
	m_grid = new Grid<int>( sWidth, sHeight, m_tileWidth, m_tileHeight );
}

void LevelMap::SetTileSize(unsigned sTileWidth, unsigned sTileHeight)
{
	m_tileWidth = sTileWidth;
	m_tileHeight = sTileHeight;
	m_grid->SetCellSize( m_tileWidth * m_scale.GetX( ), m_tileHeight * m_scale.GetY( ) );
}

void LevelMap::RegisterTileset(const Tileset& toRegister)
{
	//m_tilesets.insert(std::pair<int, Tileset > (toRegister.GetFirstGid(), toRegister));
	m_tilesets[toRegister.GetFirstGid( )] = toRegister;
}

void LevelMap::RegisterTileset(const std::string& path, const std::string& file, int fgid)
{
	Tileset tileset;
	if( tileset.Open( path, file, fgid, m_tileWidth ) )
	{
		RegisterTileset( tileset );
	}
}

void LevelMap::RegisterTile(Tile& toRegister, int locX, int locY)
{
	int fgid = toRegister.GetTilesetID( );
	std::map<int, Tileset>::iterator iter( m_tilesets.lower_bound( fgid ) );
	--iter;

	if( !m_tilesets.empty( ) )
	{
		int tileIndex = SearchTile( toRegister );

		int gid;
		if( iter != m_tilesets.end( ) )
			gid = toRegister.GetTilesetID( ) - iter->second.GetFirstGid( );
		else
		{
			gid = toRegister.GetTilesetID( );
			iter = m_tilesets.begin( );
		}


		int top = ( gid / iter->second.GetTotalColumns( this->m_tileWidth ) ) * m_tileHeight;
		int left = ( gid % iter->second.GetTotalColumns( this->m_tileWidth ) ) * m_tileWidth;

		toRegister.SetTileset( &iter->second );
		toRegister.SetRect( left, top, m_tileWidth, m_tileHeight );

		if( tileIndex == -1 )
		{
			m_tiles.insert( m_tiles.end( ), toRegister );
			tileIndex = m_tiles.size( ) - 1;
		}

		std::list<int> &tileSpot = m_grid->At( locX, locY );
		tileSpot.insert( tileSpot.end( ), tileIndex );
	}
}

void LevelMap::RegisterTileProperties(int gid, unsigned long flag)
{
	for( unsigned i = 0; i < m_tiles.size( ); ++i )
	{
		if( gid == m_tiles[i].GetTilesetID( ) )
		{
			m_tiles[i].AddToFlag( flag );
		}
	}
}

void LevelMap::ClearTiles()
{
	if( !m_grid )
		return;

	m_grid->Clear( );
}

unsigned long LevelMap::GetTilelistPropsFromRPos(unsigned int x,unsigned int y)
{
	unsigned row, column;
	m_grid->CoordinatesToIndices( ( unsigned ) x, ( unsigned ) y, row, column );

	if(row >= this->m_height || column >= this->m_width)
	{
		return 0;
	}

	return GetTilelistProperties( column, row );
}

unsigned long LevelMap::GetTilelistProperties(unsigned int x,unsigned int y)
{
	unsigned long flag = 0;

	std::list<int> &tileSpot = m_grid->At( y, x );
	std::list<int>::iterator iter( tileSpot.begin( ) ), end( tileSpot.end( ) );

	for(; iter != end; ++iter )
	{
		flag |= m_tiles[*iter].GetFlag( );
	}
	return flag;
}

int LevelMap::SearchTile(const Tile& toFind)
{
	for( unsigned i = 0; i < m_tiles.size( ); ++i )
	{
		if( toFind.GetGid( ) == m_tiles[i].GetGid( ) )
		{
			return i;
		}
	}

	return -1;
}

void LevelMap::Draw(sdl::GraphicsCore *gCore, Camera* camera)
{
	const Math::Rect *cameraRect = camera->GetRect( );
	int cameraLeft = cameraRect->GetLeft( );
	int cameraTop = cameraRect->GetTop( );
	int x, y;

	unsigned startingRow, startingColumn, endingRow, endingColumn;

	m_grid->CoordinatesToIndices( ( unsigned ) cameraLeft, ( unsigned ) cameraTop, startingRow, startingColumn );
	m_grid->CoordinatesToIndices( ( unsigned ) cameraRect->GetTotalWidth( ), ( unsigned ) cameraRect->GetTotalHeight( ), endingRow, endingColumn );
	++endingRow;
	++endingColumn;

	//keep indices inside bounds
	startingRow = std::max( 0, ( int ) startingRow );
	endingRow = std::min( m_height, ( unsigned ) endingRow );
	startingColumn = std::max( 0, ( int ) startingColumn );
	endingColumn = std::min( m_width, ( unsigned ) endingColumn );

	int startingX = startingColumn * m_tileWidth; //  * m_scale.GetX();
	int startingY = startingRow * m_tileHeight; // * m_scale.GetY();

	x = startingX;
	y = startingY;

	for( unsigned i = startingRow; i < endingRow; ++i )
	{
		for( unsigned j = startingColumn; j < endingColumn; ++j )
		{
			std::list<int> &tileSpot = m_grid->At( i, j );
			std::list<int>::iterator iter( tileSpot.begin( ) ), end( tileSpot.end( ) );

			for(; iter != end; ++iter )
			{
				Tile *t = &m_tiles[*iter];
				Tileset *ts = t->GetTileset( );

				if( ts )
				{
					m_flip[0] = t->IsFlipX( );
					m_flip[1] = t->IsFlipY( );

					gCore->Render( *ts->GetTexture( ), t->GetClip( ), gl::colors::WHITE, Math::Vector2F( x - cameraLeft, y - cameraTop ), m_scale, m_angle, m_flip );
#ifdef DEBUG_DRAW
					RenderingManager::GetInstance( )->RenderRect( Vector2F( x, y ), clip.w, clip.h, 255, 0, 0 );
#endif
				}
			}

			x += m_tileWidth; //  * m_scale.GetX();
		}

		x = startingX;
		y += m_tileHeight; //  * m_scale.GetY();
	}

	//This is the old rendering method
	/*
	x = 0;
	y = 0;

	for(unsigned i = 0; i < m_height; ++i)
	{
		for(unsigned j = 0; j < m_width; ++j)
		{
			std::list<int> &tileSpot = m_grid->At(i, j);
			std::list<int>::iterator iter(tileSpot.begin()), end(tileSpot.end());

			for(; iter != end; ++iter)
			{
				Tile *t = &m_tiles[*iter];
				Tileset *ts = t->GetTileset();

				if(ts)
				{               
					gCore->Render(*ts->GetTexture(), t->GetClip(),gl::colors::WHITE,Math::Vector2F(x - cameraLeft, y - cameraTop),m_scale,m_flip);
#ifdef DEBUG_DRAW
					RenderingManager::GetInstance()->RenderRect(Vector2F(x, y), clip.w, clip.h, 0, 255, 0);
#endif
				}
			}

			x += m_tileWidth;
		}
		x = 0;
		y += m_tileHeight;
	}
	 */


}

int LevelMap::GetWidth() const
{
	return m_width;
}

int LevelMap::GetHeight() const
{
	return m_height;
}

unsigned LevelMap::GetTileHeight() const
{
	return m_tileHeight;
}

unsigned LevelMap::GetTileWidth() const
{
	return m_tileWidth;
}

void LevelMap::SetBackground(Resource<gl::Texture>* background)
{
	if( m_background != NULL )
	{
		delete m_background;
	}
	m_background = new SelfDrawable;
	m_background->SetDisplay( background );
	m_background->SetPosition( Math::Vector2F::Zero );
	m_background->SetClip( Math::Rect( 0, 0, background->GetRawData( )->GetHeight( ), background->GetRawData( )->GetWidth( ) ) );
	m_background->SetScale( Math::Vector2F( 1, 1 ) );
	m_background->SetAlwaysVisible( true );
}

SelfDrawable* LevelMap::GetBackground() const
{
	return m_background;
}

bool LevelMap::GetTile(int gid,Tile& tile)
{
	for( unsigned i = 0; i < m_tiles.size( ); ++i )
	{
		if( gid == m_tiles[i].GetGid( ) )
		{
			tile = m_tiles[i];
			return true;
		}
	}
	return false;
}
